package cn.tangsihai.pay.service.impl;

import cn.tangsihai.basic.exception.MyException;
import cn.tangsihai.order.domain.OrderPet;
import cn.tangsihai.order.domain.OrderPetDetail;
import cn.tangsihai.order.mapper.OrderPetDetailMapper;
import cn.tangsihai.order.mapper.OrderPetMapper;
import cn.tangsihai.pay.domain.PayAlipayInfo;
import cn.tangsihai.pay.domain.PayBill;
import cn.tangsihai.pay.domain.PayFlow;
import cn.tangsihai.pay.dto.AlipayNotifyDto;
import cn.tangsihai.pay.mapper.PayAlipayInfoMapper;
import cn.tangsihai.pay.mapper.PayBillMapper;
import cn.tangsihai.pay.mapper.PayFlowMapper;
import cn.tangsihai.pay.service.IAlipayService;
import cn.tangsihai.pay.service.IPayAlipayInfoService;
import cn.tangsihai.pet.domain.Pet;
import cn.tangsihai.pet.domain.PetDetail;
import cn.tangsihai.pet.mapper.PetDetailMapper;
import cn.tangsihai.pet.mapper.PetMapper;
import com.alibaba.fastjson.JSON;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.sun.org.apache.regexp.internal.RE;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public class AlipayServiceImpl implements IAlipayService {
    @Autowired
    private IPayAlipayInfoService payAlipayInfoService;
    @Autowired
    private PayBillMapper payBillMapper;
    @Autowired
    private PayAlipayInfoMapper alipayInfoMapper;
    @Autowired
    private OrderPetDetailMapper orderPetDetailMapper;
    @Autowired
    private PayFlowMapper payFlowMapper;
    @Autowired
    private OrderPetMapper orderPetMapper;
    @Autowired
    private PetMapper petMapper;
    /**
     * 发起支付请求
     * @param orderSn
     * @return
     */
    @Override
    @Transactional
    public String alipay(String orderSn) {
        // 1.校验参数
        if (orderSn == null) {
            throw new MyException("请选择支付订单");
        }
        // 2.加载支付宝配置，从支付宝配置表pay_alipay_info中加载
        // 2.1. 设置参数（全局只需设置一次）
        Factory.setOptions(payAlipayInfoService.getOptions());
        // 3.通过订单号查询支付单
        PayBill payBill = payBillMapper.findByOrderSn(orderSn);
        if (payBill == null){
            throw new MyException("请选择支付订单");
        }

        // 准备同步回调地址
        PayAlipayInfo alipayInfo = alipayInfoMapper.findByFirst();
        if (alipayInfo == null){
            throw new MyException("请求失败");
        }
        String returnUrl = String.format(alipayInfo.getReturnUrl(), orderSn);
        // 4.发起支付请求
        try {
            // 2. 发起API调用（以创建当面付收款二维码为例）
            AlipayTradePagePayResponse response = Factory.Payment.Page()
                    .pay(payBill.getDigest(), payBill.getUnionPaySn(), payBill.getMoney().toString(), returnUrl);
            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                System.out.println("调用成功");
                // 5.处理结果
                return response.body;
            } else {
                System.err.println("调用失败，原因：" + response.body);
            }
        } catch (Exception e) {
            System.err.println("调用遭遇异常，原因：" + e.getMessage());
            throw new RuntimeException(e.getMessage(), e);
        }
        return "支付请求失败";
    }

    /**
     * 异步回调，结果处理
     * @param notifyDto
     */
    @Override
    public String calback(AlipayNotifyDto dto) {
        // 1.检验参数,略
        // 2.验证签名
        // 将对象转换整json格式的map
        Map<String, String> params = JSON.parseObject(JSON.toJSONString(dto), Map.class);
        try {
            Boolean notify = Factory.Payment.Common().verifyNotify(params);
            if (!notify){
                System.out.println("验证签名失败");
                return "fail";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 3.幂等性判断：防止支付结果被重复处理
        String out_trade_no = dto.getOut_trade_no(); // 获取订单号
        if (out_trade_no == null){
            System.out.println("无效订单号");
            return "fail";
        }

        // 4.金额是否被修改
        BigDecimal buyer = new BigDecimal(dto.getBuyer_pay_amount());
        // 根据订单号查询订单详细
        PayBill payBill = payBillMapper.findByOrderSn(out_trade_no);
        if (buyer.compareTo(payBill.getMoney()) != 0){
            System.out.println("支付错误");
            return "fail";
        }

        // 5.保存支付流水 pay_flow
        PayFlow payFlow = new PayFlow();
        payFlow.setAmount(new BigDecimal(dto.getBuyer_pay_amount()));
        BeanUtils.copyProperties(dto, payFlow);
        payFlowMapper.add(payFlow);

        // 6.修改支付单状态，在pay_bill中将待支付根据返回结果修改
        // 7.修改业务单状态，在orderPet中将支付状态根据支付返回结果修改
        // 6.1根据订单号查询支付单
        OrderPet orderPet = orderPetMapper.findByOrderSn(out_trade_no);
        // 根据订单号查询相同订单号的数据
        List<OrderPetDetail> orderPetDetails = orderPetDetailMapper.batchFindByOrderSn(out_trade_no);
        if (payBill == null){
            System.out.println("支付单错误");
            return "fail";
        }
        if (orderPet == null){
            System.out.println("支付单错误");
            return "fail";
        }
        // 支付中
        if (dto.isTradeWit()){
            System.out.println("支付中不处理");
            return "fail";
        }
        // 支付成功
        if (dto.isTradeSuccess()){
            // 修改支付单支付状态
            payBill.setPayChannel(PayBill.PAYTYPE_SUCCESS_PAY);
            // 修改业务单支付状态
            orderPet.setState(OrderPet.ORDER_WAIT_DELIVER);
        }

        // 支付失败
        if (dto.isTradeExpire()){
            // 修改支付单支付状态
            payBill.setPayChannel(PayBill.PAYTYPE_FAIL_PAY);
            // 修改业务单支付状态
            orderPet.setState(OrderPet.ORDER_PAY_FAIL);
            // 8.如果支付失败，将宠物库存退回
            List<Long> ids = orderPetDetails.stream().map(detail->detail.getPetId()).collect(Collectors.toList());
            List<Pet> pets = petMapper.batchFindById(ids);
            pets.forEach(pet -> {
                pet.setRepertory(pet.getRepertory() + 1);
                petMapper.update(pet);
            });
        }
        payBill.setLastPayTime(new Date());
        payBillMapper.update(payBill);
        orderPetMapper.update(orderPet);
        return "success";
    }
}

